package org.vaadinpa.logic.commands;

import org.vaadinpa.Conf;
import org.vaadinpa.data.Coordinate;
import org.vaadinpa.data.Fleet;
import org.vaadinpa.data.Fleet.FleetOrder;
import org.vaadinpa.data.NewsItem;
import org.vaadinpa.data.Planet;
import org.vaadinpa.data.Ship;
import org.vaadinpa.data.Ship.ShipType;
import org.vaadinpa.data.Universe;
import org.vaadinpa.gui.VaadinPA;
import org.vaadinpa.gui.util.NotificationUtil;
import org.vaadinpa.logic.Command;
import org.vaadinpa.logic.CommandHandler;
import org.vaadinpa.logic.Ticker;

public class FleetMoveCommand extends Command implements Cloneable {

	public Coordinate destination;
	public final Fleet fleet;

	public int traveled = 0;

	public FleetMoveCommand(Planet from, Planet to, Fleet fleet, FleetOrder order) {
		super(from);

		destination = to.coords;
		this.fleet = fleet;
		this.fleet.currentOrder = order;

		if (order == FleetOrder.ATTACK) {
			translateBonusFor(to.score / (double) from.score);
		} else {
			fleet.bonus = Conf.FLEET_DEFEND_MULTI;
		}

		fleet.location = planet.coords;

		translateETA(planet.coords.distanceTo(destination));

		String orders = order == FleetOrder.ATTACK ? "hostile" : "friendly";
		NewsItem item = new NewsItem("Fleet launch detected", "A wormhole from " + from + " was detected. You have "
				+ orders + " incoming, eta " + fleet.eta + " ticks (tick " + (Ticker.currentTick + fleet.eta) + ").");
		to.addNews(item);
	}

	private void translateETA(int distanceTo) {
		if (distanceTo <= 2) {
			fleet.eta = 2;
		} else if (distanceTo <= 4) {
			fleet.eta = 3;
		} else if (distanceTo <= 7) {
			fleet.eta = 4;
		} else {
			fleet.eta = 5;
		}

		// increase travel time if there are battleships in the fleet
		for (Ship s : Ship.getAvailableShips()) {
			if (s.getType() == ShipType.BATTLESHIP && fleet.get(s) > 0) {
				fleet.eta += 1;
				break;
			}
		}
	}

	private void translateBonusFor(double d) {
		if (d < 0.5) {
			fleet.bonus = 0.0;
		} else if (d < 0.7) {
			fleet.bonus = 0.6;
		} else if (d < 0.8) {
			fleet.bonus = 0.7;
		} else if (d < 0.9) {
			fleet.bonus = 0.85;
		} else if (d < 1.1) {
			fleet.bonus = 1.0;
		} else if (d < 1.2) {
			fleet.bonus = 1.1;
		} else if (d < 1.4) {
			fleet.bonus = 1.3;
		} else {
			fleet.bonus = 1.5;
		}
	}

	public void resetFleet() {
		fleet.eta = 0;
		fleet.location = planet.coords;
		fleet.currentOrder = FleetOrder.DEFEND;
	}

	@Override
	public void execute() {
		// executes before battle

		// check if fleet has been destroyed
		// TODO should be checked after battle last tick
		if (fleet.isEmpty()) {

			resetFleet();
			return;
		}

		if (traveled == 0) {
			// launch
			fleet.location = null;

			// Launch newsitem
			NewsItem i;
			if (destination.equals(planet.coords)) {
				i = new NewsItem("Fleet returning", "Fleet " + fleet.name + " is on it's way home.");
			} else {
				i = new NewsItem("Fleet order issued", "Fleet " + fleet.name + " was dispatched to "
						+ getName(destination) + " with orders to " + fleet.currentOrder.toString());
			}
			planet.addNews(i);
		}

		traveled++;

		if (cancelNextTick) {

			// turn around
			destination = planet.coords;
			traveled = fleet.eta - traveled;
			fleet.currentOrder = FleetOrder.DEFEND;

			NewsItem i = new NewsItem("Recall order issued", "Fleet " + fleet.name + " was recalled");
			planet.addNews(i);

		} else if (traveled == fleet.eta) {
			// arrival at a planet

			// update position, inits possible battle
			fleet.location = destination;

			// turn around (or remain at planet)
			destination = planet.coords;
			traveled = 0;
		}

		// Returned to home?
		if (fleet.atPlanet(planet.coords)) {
			NewsItem i = new NewsItem("Fleet returned", "Fleet " + fleet.name + " has returned");
			planet.addNews(i);
			fleet.currentOrder = FleetOrder.DEFEND;
			fleet.eta = 0;
		} else {
			// not home yet
			FleetMoveCommand clone = (FleetMoveCommand) clone();
			CommandHandler.handle(clone);
		}
	}

	@Override
	public Object clone() {
		try {
			return super.clone();
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return null;
	}

	@Override
	public void cancel() {

		if (destination.equals(planet)) {
			NotificationUtil.notify("Fleet already returning", "");
			return;
		}

		// if fleet hasn't moved yet (from home planet) we can cancel the order
		// immediately
		if (!destination.equals(VaadinPA.getPlanet()) && traveled == 0) {
			fleet.location = planet.coords;
			fleet.eta = 0;
			fleet.currentOrder = FleetOrder.DEFEND;
		} else {
			throw new UnsupportedOperationException("Cancel called on a non-removeable command");
		}
	}

	@Override
	public boolean canBeCanceled() {
		if (destination.equals(planet.coords)) {
			NotificationUtil.notify("Fleet already returning", "");
			return false;
		}
		return true;
	}

	@Override
	public String getDescription() {
		return fleet.name;
	}

	@Override
	public String getName() {

		if (destination.equals(planet.coords)) {
			fleet.currentOrder = FleetOrder.DEFEND;
		}

		switch (fleet.currentOrder) {
		case ATTACK:
			return "Attack " + getName(destination) + ", eta " + getETA();
		case DEFEND:
			if (destination.equals(planet.coords)) {
				return "Return, eta " + getETA();
			} else {
				return "Defend " + getName(destination) + ", eta " + getETA();
			}
		default:
			return "";
		}
	}

	private int getETA() {
		return fleet.eta - traveled;
	}

	private String getName(Coordinate c) {
		return Universe.instance.get(destination).name + " (" + c.toString() + ")";
	}

	@Override
	public void serialize(StringBuilder sb) {
		super.serializeBase(sb);

		sb.append("<traveled>" + traveled + "</traveled>");
		sb.append("<destination x=\"" + destination.x + "\" y=\"" + destination.y + "\"/>");
		sb.append("<fleet name=\"" + fleet.name + "\"/>");

		sb.append("</" + this.getClass().getSimpleName() + ">");
	}
}
